package com.andaily.service.impl;

import com.andaily.domain.dto.thing.*;
import com.andaily.domain.shared.DateUtils;
import com.andaily.domain.shared.paginated.PaginatedLoader;
import com.andaily.domain.shared.security.SecurityUtils;
import com.andaily.domain.thing.*;
import com.andaily.domain.user.ThingSetting;
import com.andaily.domain.user.User;
import com.andaily.domain.user.UserSetting;
import com.andaily.infrastructure.mybatis.data.ThingStatisticsData;
import com.andaily.service.ThingService;
import com.andaily.service.UserService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;

/**
 * @author Shengzhao Li
 */
public class ThingServiceImpl implements ThingService {

    @Autowired
    private ThingRepository thingRepository;
    @Autowired
    private UserService userService;

    @Override
    public EventDto loadEventDto(String guid) {
        if (StringUtils.isNotEmpty(guid)) {
            Event event = thingRepository.findEventByGuid(guid);
            return new EventDto(event);
        }
        return new EventDto();
    }

    @Override
    public void persistEventDto(EventDto eventDto) {
        Event event = eventDto.instance();
        final User creator = userService.currentUser();
        event.update(eventDto.getName(), eventDto.getDescription()).updateCreator(creator);
        event.saveOrUpdate();
    }

    @Override
    public ThingDto loadThingDto(String guid) {
        ThingDto thingDto;
        final String userGuid = SecurityUtils.currentUserGuid();
        if (StringUtils.isNotEmpty(guid)) {
            final Thing thing = thingRepository.findByGuid(guid);
            thingDto = new ThingDto(thing);
        } else {
            ThingStatus status = thingRepository.findDefaultStatus(userGuid);
            thingDto = new ThingDto(status);
        }
        List<Event> events = thingRepository.findAvailableEvents(userGuid);
        thingDto.setAvailableEvents(EventDto.toDtos(events));
        return thingDto;
    }

    @Override
    public void persistThingDto(ThingDto thingDto) {
        Thing thing = thingDto.instance();
        final User owner = userService.currentUser();
        thing.update(thingDto).updateOwner(owner).saveOrUpdate();
    }

    @Override
    public int loadTotalThing() {
        return thingRepository.totalThings();
    }

    @Override
    public EventOverviewDto loadEventOverviewDto(EventOverviewDto overviewDto) {
        final Map<String, Object> params = overviewDto.params();
        return overviewDto.load(new PaginatedLoader<EventDto>() {
            @Override
            public List<EventDto> loadList() {
                List<Event> events = thingRepository.findEvents(params);
                return EventDto.toDtos(events);
            }

            @Override
            public int loadTotalSize() {
                return thingRepository.totalEvents(params);
            }
        });
    }

    @Override
    public ThingAmountDto loadThingAmountDto() {
        ThingAmountDtoLoader thingAmountDtoLoader = new ThingAmountDtoLoader();
        return thingAmountDtoLoader.load();
    }

    @Override
    public void deleteEvent(String guid) {
        thingRepository.archiveEvent(guid, true);
    }

    @Override
    public ThingMonitoringDto loadThingMonitoringDto(ThingMonitoringDto thingMonitoringDto) {
        final Map<String, Object> params = thingMonitoringDto.params();
        return thingMonitoringDto.load(new PaginatedLoader<ThingDto>() {
            @Override
            public List<ThingDto> loadList() {
                List<Thing> things = thingRepository.findThings(params);
                return ThingDto.toDtos(things);
            }

            @Override
            public int loadTotalSize() {
                return thingRepository.amountOfThings(params);
            }
        });
    }

    @Override
    public ThingDto loadExistThingDto(String guid) {
        Thing thing = thingRepository.findByGuid(guid);
        return new ThingDto(thing);
    }

    @Override
    public boolean hasHighThings() {
        final String userGuid = SecurityUtils.currentUserGuid();
        int count = thingRepository.countThingsOfPriorities(userGuid, ThingPriority.HIGH, ThingPriority.HIGHER);
        return count > 0;
    }

    @Override
    public void changeThingStatus(String guid, ThingStatus status) {
        thingRepository.updateThingStatus(guid, status);
        if (ThingStatus.CANCEL.equals(status)) {
            thingRepository.updateThingCancelTime(guid, DateUtils.now());
        } else if (ThingStatus.END.equals(status)) {
            thingRepository.updateThingEndTime(guid, DateUtils.now());
        }
    }

    @Override
    public ThingHistoryDto loadThingHistoryDto(ThingHistoryDto thingHistoryDto) {
        final Map<String, Object> params = thingHistoryDto.params();
        return thingHistoryDto.load(new PaginatedLoader<ThingDto>() {
            @Override
            public List<ThingDto> loadList() {
                List<Thing> things = thingRepository.findThings(params);
                return ThingDto.toDtos(things);
            }

            @Override
            public int loadTotalSize() {
                return thingRepository.amountOfThings(params);
            }
        });
    }

    @Override
    public ThingWarningDto loadThingWarningDto(ThingWarningDto thingWarningDto) {
        final Map<String, Object> params = thingWarningDto.params();
        return thingWarningDto.load(new PaginatedLoader<ThingDto>() {
            @Override
            public List<ThingDto> loadList() {
                List<Thing> things = thingRepository.findWarningThings(params);
                return ThingDto.toDtos(things);
            }

            @Override
            public int loadTotalSize() {
                return thingRepository.totalWarningThings(params);
            }
        });
    }

    @Override
    public ThingArchiveDto loadThingArchiveDto(ThingArchiveDto thingArchiveDto) {
        final Map<String, Object> params = thingArchiveDto.params();
        return thingArchiveDto.load(new PaginatedLoader<ThingDto>() {
            @Override
            public List<ThingDto> loadList() {
                List<Thing> things = thingRepository.findThings(params);
                return ThingDto.toDtos(things);
            }

            @Override
            public int loadTotalSize() {
                return thingRepository.amountOfThings(params);
            }
        });
    }

    @Override
    public boolean archiveThings(String[] guids) {
        if (guids == null || guids.length == 0) {
            return false;
        }
        thingRepository.archiveThings(guids, true);
        return true;
    }

    @Override
    public ThingSearchDto loadSearchResult(ThingSearchDto thingSearchDto) {
        final Map<String, Object> params = thingSearchDto.params();
        return thingSearchDto.load(new PaginatedLoader<ThingDto>() {
            @Override
            public List<ThingDto> loadList() {
                List<Thing> things = thingRepository.findSearchThings(params);
                return ThingDto.toDtos(things);
            }

            @Override
            public int loadTotalSize() {
                return thingRepository.totalSearchThings(params);
            }
        });
    }

    @Override
    public ThingSettingDto loadThingSettingDto() {
        final ThingSetting thingSetting = findUserSetting().thingSetting();
        return (thingSetting == null ? new ThingSettingDto() : new ThingSettingDto(thingSetting));
    }

    @Override
    public void updateThingSettingDto(ThingSettingDto thingSettingDto) {
        String userGuid = SecurityUtils.currentUserGuid();
        ThingSetting thingSetting = thingSettingDto.instance();
        thingRepository.updateThingSetting(thingSetting, userGuid);
    }

    @Override
    public ThingStatisticsDto loadStatusStatisticsDto() {
        String userGuid = SecurityUtils.currentUserGuid();
        List<ThingStatisticsData> datas = thingRepository.findStatusStatisticsDatas(userGuid);
        return new ThingStatisticsDto(datas);
    }

    @Override
    public ThingStatisticsDto loadPriorityStatisticsDto() {
        String userGuid = SecurityUtils.currentUserGuid();
        List<ThingStatisticsData> datas = thingRepository.findPriorityStatisticsDatas(userGuid);
        return new ThingStatisticsDto(datas);
    }

    private UserSetting findUserSetting() {
        String userGuid = SecurityUtils.currentUserGuid();
        return userService.loadUserSettingByUserGuid(userGuid);
    }
}
